package services

import (
	"context"
	"crypto/subtle"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/google/uuid"
	"github.com/redis/go-redis/v9"

	"github.com/samaa/photosalesplus/backend/internal/config"
	"github.com/samaa/photosalesplus/backend/internal/models"
)

const tokenPrefix = "auth:tokens:"

type AuthService interface {
	Login(ctx context.Context, username, password string) (string, *models.User, error)
	Validate(ctx context.Context, token string) (uint, bool)
	Logout(ctx context.Context, token string) error
	EnsureMerchantAccount(ctx context.Context) error
}

type authService struct {
	cfg         *config.Config
	redis       *redis.Client
	userService UserService
}

func NewAuthService(cfg *config.Config, redis *redis.Client, userService UserService) AuthService {
	return &authService{
		cfg:         cfg,
		redis:       redis,
		userService: userService,
	}
}

func (s *authService) Login(ctx context.Context, username, password string) (string, *models.User, error) {
	username = strings.TrimSpace(username)

	configUsername := strings.TrimSpace(s.cfg.AuthUsername)

	if configUsername != "" && username == configUsername {
		configPassword := s.cfg.AuthPassword
		if subtle.ConstantTimeCompare([]byte(password), []byte(configPassword)) != 1 {
			return "", nil, ErrPasswordWrong
		}

		merchantUser, err := s.ensureMerchantUser(ctx, configUsername)
		if err != nil {
			return "", nil, err
		}

		token, err := s.issueToken(ctx, merchantUser.ID)
		if err != nil {
			return "", nil, err
		}
		return token, merchantUser, nil
	}

	user, err := s.userService.Authenticate(ctx, username, password)
	if err != nil {
		return "", nil, err
	}

	token, err := s.issueToken(ctx, user.ID)
	if err != nil {
		return "", nil, err
	}

	return token, user, nil
}

func (s *authService) Validate(ctx context.Context, token string) (uint, bool) {
	if token == "" {
		return 0, false
	}
	key := tokenPrefix + token
	val, err := s.redis.Get(ctx, key).Result()
	if err != nil {
		return 0, false
	}
	id, err := strconv.ParseUint(val, 10, 64)
	if err != nil {
		return 0, false
	}
	return uint(id), true
}

func (s *authService) Logout(ctx context.Context, token string) error {
	if token == "" {
		return nil
	}
	key := tokenPrefix + token
	return s.redis.Del(ctx, key).Err()
}

func (s *authService) issueToken(ctx context.Context, userID uint) (string, error) {
	token := uuid.NewString()
	key := tokenPrefix + token
	if err := s.redis.Set(ctx, key, fmt.Sprintf("%d", userID), s.cfg.TokenTTL).Err(); err != nil {
		return "", err
	}
	return token, nil
}

func (s *authService) ensureMerchantUser(ctx context.Context, username string) (*models.User, error) {
    user, err := s.userService.FindByUsername(ctx, username)
    if err == nil && user != nil {
        if user.Role != "merchant" {
            _ = s.userService.UpdateRole(ctx, user.ID, "merchant")
            user.Role = "merchant"
        }
        return user, nil
    }

	now := time.Now()
    merchant := &models.User{
        Username:     username,
        DisplayName:  "商户账号",
        Email:        "",
        Role:         "merchant",
        PasswordHash: "",
        CreatedAt:    now,
        UpdatedAt:    now,
    }

	if err := s.userService.Create(ctx, merchant); err != nil {
		return nil, err
	}

	return merchant, nil
}

func (s *authService) EnsureMerchantAccount(ctx context.Context) error {
	username := strings.TrimSpace(s.cfg.AuthUsername)
	if username == "" {
		return nil
	}
	_, err := s.ensureMerchantUser(ctx, username)
	return err
}
